package com.yangxk.kadmin.generator.internal;

import com.yangxk.kadmin.generator.config.PackageConfig;
import com.yangxk.kadmin.generator.config.RelateTableConfig;
import com.yangxk.kadmin.generator.config.SuperConfig;
import com.yangxk.kadmin.generator.config.TableConfig;
import com.yangxk.kadmin.generator.constant.Const;
import com.yangxk.kadmin.generator.constant.Vue;
import com.yangxk.kadmin.generator.field.Field;
import com.yangxk.kadmin.generator.field.PrimaryKeyField;
import com.yangxk.kadmin.generator.internal.field.BetweenFieldInternal;
import com.yangxk.kadmin.generator.rules.Match;
import com.yangxk.kadmin.generator.rules.ValidType;
import com.yangxk.kadmin.generator.template.TemplatePath;
import com.yangxk.kadmin.generator.template.TemplatePathFactory;
import com.yangxk.kadmin.generator.template.TemplateType;
import com.yangxk.kadmin.generator.url.RequestUrl;
import com.yangxk.kadmin.generator.url.Url;
import lombok.Getter;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

import static com.yangxk.kadmin.generator.util.Tools.*;

/**
 * 配置汇总
 * @author yangxk
 * @date 2018/10/4 20:11
 */
@Getter
public class ConfigInternal {

    private Url url;

    private String tableName; // 表名称
    private String modelName; // 模块名称
    private String modelNameLower; // 模块名称小写
    private String zhModelName; // 模块中文名称
    private boolean dataScope; // 是否生成数据权限
    private boolean queryAll; // 是否生成queryAll()方法
    private boolean relate; // 是否是关联表类型

    private Map<String, Object> otherConfig;

    private Map<String, String> superInfo;

    private Map<String, String> packageInfo;

    private Map<String, String> pathInfo;

    private Map<String, String> names;

    private Map<String, String> templatePath;

    private Map<String, String> requestUrl;

    private Map<String, String> authCode;

    private PrimaryKeyField pkField;

    private List<Field> poFields;
    private Set<String> poImported;

    private List<Field> pojoFields;
    private Set<String> pojoImported;

    private List<Field> voFields;
    private Set<String> voImported;

    private List<Field> queryFormFields;
    private Set<String> queryFormImported;

    private List<Field> editFormFields;
    private Set<String> editFormImported;

    // 关联表相关信息
    private String relateTable;
    private String relateModelName;
    private String relateModelNameFirstLower;
    private String relateTableField;
    private String tableRelateField;
    private String relateTablePropertyName;
    private String tableRelatePropertyName;
    private String tableAlias;
    private String relateTableAlias;
    private List<Field> relateTableSqlField;

    public ConfigInternal(PackageConfig packageConfig, SuperConfig superConfig,
                          TableConfig tableConfig, TemplateType templateType, Map<String, Object> otherConfig) {
        if (tableConfig == null)
            throw new RuntimeException("缺少表相关配置");
        if (packageConfig == null)
            packageConfig = new PackageConfig();
        if (superConfig == null)
            superConfig = new SuperConfig();
        if (templateType == null)
            templateType = TemplateType.crud;

        this.modelName = tableConfig.getModelName();
        this.modelNameLower = first2Lower(tableConfig.getModelName());
        this.zhModelName = tableConfig.getZhModelName();
        this.tableName = tableConfig.getTableName();
        this.url = RequestUrl.getUrl(templateType, modelName, zhModelName);
        this.dataScope = tableConfig.isDataScope();
        this.queryAll = tableConfig.isQueryAll();
        this.relate = getRelateType(templateType);
        this.otherConfig = otherConfig;

        handlePackage(packageConfig);
        handleSuperClass(superConfig);
        handleNames(this.modelName);
        handleTemplatePath(templateType);
        handleVue(this.modelName);
        handleField(tableConfig.getFields());
        handleRequestUrl();

        handleRelateTable(tableConfig.getRelateTableConfig());
    }

    // 判断是否是关联表类型
    private boolean getRelateType(TemplateType templateType) {
        if (templateType == TemplateType.relate)
            return true;
        return false;
    }

    private void handleRelateTable(RelateTableConfig relateTableConfig) {
        if (relateTableConfig == null)
            return ;

        this.relateTableAlias = "t2";
        this.tableAlias = "t1";
        this.relateTable = relateTableConfig.getTableName();
        this.relateTableField = relateTableConfig.getFieldName();
        this.tableRelateField = relateTableConfig.getParentTableField();
        this.relateTablePropertyName = relateTableConfig.getPropertyName();
        this.tableRelatePropertyName = relateTableConfig.getParentTablePropertyName();
        this.relateModelName = relateTableConfig.getModelName();
        this.relateModelNameFirstLower = first2Lower(relateTableConfig.getModelName());

        relateTableSqlField = new ArrayList<>();
        for (Field field : relateTableConfig.getColumns()) {
            if (field.isListShow()) {
                voFields.add(field);
                relateTableSqlField.add(field);
            }

            if (field.isEdit())
                editFormFields.add(field);

            if (field.isQuery()) {
                queryFormFields.add(field);
                pojoFields.add(field);
            }
        }
    }

    private void handleRequestUrl() {
        requestUrl = url.getRequestUrl();
        authCode = url.getAuthCode();
    }

    private void handleField(List<Field> fields) {
        poFields = new ArrayList<>();
        pojoFields = new ArrayList<>();
        voFields = new ArrayList<>();
        queryFormFields = new ArrayList<>();
        editFormFields = new ArrayList<>();

        for (Field field : fields) {
            if (field.isPrimaryKey()) {
                pkField = PrimaryKeyField.builder()
                            .propertyName(field.getPropertyName())
                            .propertyType(field.getPropertyType())
                            .propertyShortType(field.getPropertyShortType())
                            .columnName(field.getColumnName())
                            .jdbcType(field.getJdbcType())
                            .valid(field.getValidType())
                            .build();

                voFields.add(field);
            }

            if (field.isListShow())
                voFields.add(field);

            if (field.isEdit())
                editFormFields.add(field);

            if (field.isQuery()) {
                if (field.getMatch() == Match.between) {
                    List<Field> bt = BetweenFieldInternal.createField(field);
                    queryFormFields.addAll(bt);
                    pojoFields.addAll(bt);
                } else {
                    queryFormFields.add(field);
                    pojoFields.add(field);
                }
            }


            poFields.add(field);
        }

        poImported = getFieldTypeImport(poFields);
        pojoImported = getFieldTypeImport(pojoFields);
        voImported = getFieldTypeImport(voFields);
        queryFormImported = getFieldTypeImport(queryFormFields);

        editFormImported = new HashSet<>();
        editFormImported.addAll(getFieldTypeImport(editFormFields));
        editFormImported.addAll(getFieldValidImport(editFormFields));
    }

    private void handleVue(String modelName) {
        pathInfo.put(Const.VUE_API_PATH, joinPath(getVueProjectPath(), Vue.VUE_API_PATH));
        pathInfo.put(Const.VUE_PAGE_PATH, joinPath(getVueProjectPath(),  Vue.VUE_PAGE_PATH + File.separator + first2Lower(modelName)));
        names.put(Const.VUE_PAGE_NAME, modelName);
        names.put(Const.VUE_API_NAME, first2Lower(modelName));
    }

    private void handleTemplatePath(TemplateType templateType) {
        TemplatePath template = TemplatePathFactory.getTemplate(templateType);
        templatePath = template.getTemplatePath();
        if (this.dataScope) {
            templatePath.put(Const.VUE_PAGE_TEMPLATE, templatePath.get(Const.VUE_PAGE_DATASCOPE_TEMPLATE));
        }
    }


    public String getVueProjectPath() {
        File file = new File(getProjectRootPath());
        File vue = new File(file.getParentFile().getAbsolutePath(), Vue.VUE_PROJECT_NAME);
        String vuePath = vue.getAbsolutePath();
        return vuePath;
    }

    private void handleNames(String modelName) {
        names = new HashMap<>();
        names.put(Const.MAPPER_CLASS_NAME, modelName + "Mapper");
        names.put(Const.XML_CLASS_NAME, modelName + "Mapper");
        names.put(Const.SERVICE_CLASS_NAME, modelName + "Service");
        names.put(Const.SERVICE_IMPL_CLASS_NAME, modelName + "ServiceImpl");
        names.put(Const.PO_CLASS_NAME, modelName);
        names.put(Const.POJO_CLASS_NAME, modelName + "Query");
        names.put(Const.VO_CLASS_NAME, modelName + "Vo");
        names.put(Const.QUERY_FORM_CLASS_NAME, modelName + "QueryForm");
        names.put(Const.EDIT_FORM_CLASS_NAME, modelName + "EditForm");
        names.put(Const.CONTROLLER_CLASS_NAME, modelName + "Controller");

        names.put(Const.MAPPER_PROPERTY_NAME, first2Lower(names.get(Const.MAPPER_CLASS_NAME)));
        names.put(Const.SERVICE_PROPERTY_NAME, first2Lower(names.get(Const.SERVICE_CLASS_NAME)));
        names.put(Const.SERVICE_IMPL_PROPERTY_NAME, first2Lower(names.get(Const.SERVICE_IMPL_CLASS_NAME)));
        names.put(Const.PO_PROPERTY_NAME, first2Lower(names.get(Const.PO_CLASS_NAME)));
        names.put(Const.POJO_PROPERTY_NAME, first2Lower(names.get(Const.POJO_CLASS_NAME)));
        names.put(Const.VO_PROPERTY_NAME, first2Lower(names.get(Const.VO_CLASS_NAME)));
        names.put(Const.QUERY_FORM_PROPERTY_NAME, first2Lower(names.get(Const.QUERY_FORM_CLASS_NAME)));
        names.put(Const.EDIT_FORM_PROPERTY_NAME, first2Lower(names.get(Const.EDIT_FORM_CLASS_NAME)));
        names.put(Const.CONTROLLER_PROPERTY_NAME, first2Lower(names.get(Const.CONTROLLER_CLASS_NAME)));
    }

    private void handleSuperClass(SuperConfig config) {
        superInfo = new HashMap<>();
        superInfo.put(Const.SUPER_MAPPER, config.getSuperMapperClass());
        superInfo.put(Const.SUPER_SERVICE, config.getSuperServiceClass());
        superInfo.put(Const.SUPER_SERVICE_IMPL, config.getSuperServiceImplClass());
        superInfo.put(Const.SUPER_PO, config.getSuperPoClass());
        superInfo.put(Const.SUPER_CONTROLLER, config.getSuperControllerClass());

        superInfo.put(Const.SUPER_MAPPER_CLASS_NAME, getClassName(config.getSuperMapperClass()));
        superInfo.put(Const.SUPER_SERVICE_CLASS_NAME, getClassName(config.getSuperServiceClass()));
        superInfo.put(Const.SUPER_SERVICE_IMPL_CLASS_NAME, getClassName(config.getSuperServiceImplClass()));
        superInfo.put(Const.SUPER_PO_CLASS_NAME, getClassName(config.getSuperPoClass()));
        superInfo.put(Const.SUPER_CONTROLLER_CLASS_NAME, getClassName(config.getSuperControllerClass()));
    }

    private void handlePackage(PackageConfig config) {
        packageInfo = new HashMap<>();
        String basePackage = config.getBasePackage();
        packageInfo.put(Const.MAPPER_PACKAGE, joinPakcage(basePackage, config.getMapper()));
        packageInfo.put(Const.SERVICE_PACKAGE, joinPakcage(basePackage, config.getService()));
        packageInfo.put(Const.SERVICE_IMPL_PACKAGE, joinPakcage(basePackage, config.getServiceImpl()));
        packageInfo.put(Const.CONTROLLER_PACKAGE, joinPakcage(basePackage, config.getController()));
        packageInfo.put(Const.PO_PACKAGE, joinPakcage(basePackage, config.getPo()));
        packageInfo.put(Const.POJO_PACKAGE, joinPakcage(basePackage, config.getPojo()));
        packageInfo.put(Const.VO_PACKAGE, joinPakcage(basePackage, config.getVo()));
        packageInfo.put(Const.EDIT_FORM_PACKAGE, joinPakcage(basePackage, config.getForm()));
        packageInfo.put(Const.QUERY_FORM_PACKAGE, joinPakcage(basePackage, config.getForm()));

        pathInfo = new HashMap<>();
        String basePath = getMavenJavaSrcPath();
        pathInfo.put(Const.MAPPER_PATH, joinPath(basePath, package2Path(packageInfo.get(Const.MAPPER_PACKAGE))));
        pathInfo.put(Const.XML_PATH, joinPath(getMavenResourcesPath(), Const.XML_RESOURCES_DIR));
        pathInfo.put(Const.SERVICE_PATH, joinPath(basePath, package2Path(packageInfo.get(Const.SERVICE_PACKAGE))));
        pathInfo.put(Const.SERVICE_IMPL_PATH, joinPath(basePath, package2Path(packageInfo.get(Const.SERVICE_IMPL_PACKAGE))));
        pathInfo.put(Const.CONTROLLER_PATH, joinPath(basePath, package2Path(packageInfo.get(Const.CONTROLLER_PACKAGE))));
        pathInfo.put(Const.PO_PATH, joinPath(basePath, package2Path(packageInfo.get(Const.PO_PACKAGE))));
        pathInfo.put(Const.POJO_PATH, joinPath(basePath, package2Path(packageInfo.get(Const.POJO_PACKAGE))));
        pathInfo.put(Const.VO_PATH, joinPath(basePath, package2Path(packageInfo.get(Const.VO_PACKAGE))));
        pathInfo.put(Const.EDIT_FORM_PATH, joinPath(basePath, package2Path(packageInfo.get(Const.EDIT_FORM_PACKAGE))));
        pathInfo.put(Const.QUERY_FORM_PATH, joinPath(basePath, package2Path(packageInfo.get(Const.QUERY_FORM_PACKAGE))));

    }

    /**
     * 获取字段非lang包的下类型
     * lang包下的类型无需导入，其他包下的需要导入
     */
    private Set<String> getFieldTypeImport(List<Field> fields) {
        if (fields == null || fields.isEmpty())
            return new HashSet<>();
        return fields.stream().filter(item -> !item.getPropertyType().startsWith("java.lang"))
                .map(item -> item.getPropertyType())
                .collect(Collectors.toSet());
    }

    /**
     * 获取验证模式需要导入的类
     */
    private Set<String> getFieldValidImport(List<Field> fields) {
        if (fields == null || fields.isEmpty())
            return new HashSet<>();
        Set<String> result = fields.stream().filter(item -> item.getValidType() != null)
                .map(item -> item.getValidType().getValidClass())
                .collect(Collectors.toSet());
        long remark = fields.stream().filter(item -> item.getPropertyName().equals("remark")).count();
        if (remark > 0) {
            result.add(ValidType.size.getValidClass());
        }

        return result;
    }
}
